package com.ceair.flight.refund.location.service.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.ceair.flight.refund.dto.external.BaseRefundRequest;
import com.ceair.flight.refund.dto.external.ExternalRequestFactory;
import com.ceair.flight.refund.dto.external.RefundPriceResultList;
import com.ceair.flight.refund.dto.external.RefundableRequest;
import com.ceair.flight.refund.dto.external.RefundableResultList;
import com.ceair.flight.refund.dto.external.RequestBuilder;
import com.ceair.flight.refund.dto.external.ResultBuilder;
import com.ceair.flight.refund.dto.external.TicketRefundableResult;
import com.ceair.flight.refund.location.dao.IRefundApplyDao;
import com.ceair.flight.refund.location.model.RefundApplyDO;
import com.ceair.flight.refund.location.service.IRefundLocationService;
import com.ceair.flight.refund.service.external.calculator.IMuServiceRefundCalculator;
import com.ceair.flight.refund.service.external.refundcheck.IRefundableCheckerService;
import com.ceair.muservice.basicdata.dto.airport.AirportDto;
import com.ceair.muservice.basicdata.dto.channel.ChannelManageDto;
import com.ceair.muservice.basicdata.dto.channel.ChannelRQ;
import com.ceair.muservice.basicdata.feign.util.AirportUtil;
import com.ceair.muservice.basicdata.feign.util.ChannelUtil;
import com.ceair.muservice.common.exception.MuServiceException;

@Service
public class RefundLocationServiceImpl implements IRefundLocationService {

    private static final Logger LOG = LoggerFactory.getLogger(RefundLocationServiceImpl.class);

    @Autowired
    private IRefundApplyDao refundApplyDao;
    @Autowired
    private IMuServiceRefundCalculator localRefundCalculator;
    @Autowired
    private IRefundableCheckerService checkerService;

    @Override
    public RefundPriceResultList calculatePrice(RefundableRequest request) throws MuServiceException {
        try {
            LOG.debug("Calculating price for request: {}", request);
            RefundPriceResultList result = localRefundCalculator.calculatePrice(request);
            LOG.debug("Calculate refund price response: {}", result);
            return result;
        } catch (Exception e) {
            LOG.error("退票失败", e);
            throw new MuServiceException(e);
        }
    }

    @Override
    public RefundableResultList checkRefundable(String flightOrderNo, Set<String> tikectSet,String createId,String ffpNo) {
        if (tikectSet.isEmpty()) {
            LOG.debug("No tickets need to check refundable");
            return null;
        }

        RefundableRequest refundableRequest = new RefundableRequest(flightOrderNo, tikectSet);
        refundableRequest.setCreateId(createId);
        refundableRequest.setFfpNo(ffpNo);
        LOG.info("Check ticket refundable user request: {}", refundableRequest);
        refundableRequest.check();
        BaseRefundRequest baseRefundRequest = RequestBuilder.INSTANCE.buildTicketRefundRequest(refundableRequest);
        TicketRefundableResult ticketRefundableResult = null;
        try {
            ticketRefundableResult = checkerService.checkRefundable(baseRefundRequest);
            LOG.debug("Refundable checker response: {}", ticketRefundableResult);
            if (ticketRefundableResult == null) {
                LOG.error("Check tickets {} whether refundable had null response", Arrays.toString(tikectSet.toArray()));
                return null;
            }
        } catch (Exception e) {
            LOG.error("Check tickets " + Arrays.toString(tikectSet.toArray()) + " refundable error", e);
            return null;
        }finally {
            //clear cache
            ExternalRequestFactory.clear(baseRefundRequest);
        }
        RefundableResultList refundableResultList = ResultBuilder.INSTANCE.buildRefundableResult(ticketRefundableResult);
        return refundableResultList;
    }

    @Override
    public RefundPriceResultList calculateVolPrice(RefundableRequest request) throws MuServiceException {
        try {
            LOG.debug("Calculating volrefund price for request: {}", request);
            com.ceair.flight.refund.dto.external.RefundableRequest req = new com.ceair.flight.refund.dto.external.RefundableRequest();
            req.setFlightOrderNo(request.getFlightOrderNo());
            req.setTickets(request.getTickets());
            req.setSalesChannel(request.getSalesChannel());
            RefundPriceResultList result = localRefundCalculator.calculateVolPrice(req);
            LOG.debug("Calculate volrefund price response: {}", result);
            return result;
        } catch (Exception e) {
            LOG.error("计算自愿退票费失败", e);
            throw new MuServiceException(e);
        }
    }

    @Override
    public RefundPriceResultList calculateInVolPrice(RefundableRequest request) throws MuServiceException {
        try {
            LOG.debug("Calculating involrefund price for request: {}", request);
            com.ceair.flight.refund.dto.external.RefundableRequest req = new com.ceair.flight.refund.dto.external.RefundableRequest();
            req.setFlightOrderNo(request.getFlightOrderNo());
            req.setTickets(request.getTickets());
            req.setSalesChannel(request.getSalesChannel());
            RefundPriceResultList result = localRefundCalculator.calculateInvolPrice(req);
            LOG.debug("Calculate involrefund price response: {}", result);
            return result;
        } catch (Exception e) {
            LOG.error("计算自愿退票费失败", e);
            throw new MuServiceException(e);
        }

    }

    @Override
    public ChannelManageDto findChannelByCode(String channelCode) {
        ChannelRQ rq = new ChannelRQ();
        rq.setChannelCode(channelCode);
        ChannelManageDto channelManageDto = ChannelUtil.findChannelByChannelCode(channelCode);
        return channelManageDto;
    }

    @Override
    public Map<String, AirportDto> findNationCityAirNameByCodes(List<String> codeList) {
        Map<String, AirportDto> result = new HashMap<>();
        List<AirportDto> nationCityAirportDtoList = AirportUtil.findByCodeList(codeList);
        if (!CollectionUtils.isEmpty(nationCityAirportDtoList)){
            for (AirportDto nationCityAirportDto:nationCityAirportDtoList){
                result.put(nationCityAirportDto.getCode(),nationCityAirportDto);
            }
        }
        return result;
    }

    @Override
    public String getExternalData(String path, String param) {
        LOG.info("调用外部接口，url: {}; param: {}", path, param);
        URL url = null;
        HttpURLConnection httpURLConnection = null;
        StringBuffer sb = new StringBuffer("");
        try {
            url = new URL(path);
            httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setRequestMethod("POST");
            httpURLConnection.setRequestProperty("Accept", "application/json");
            httpURLConnection.setRequestProperty("Content-Type", "application/json");
            OutputStream outputStream = httpURLConnection.getOutputStream();
            outputStream.write(param.getBytes());
            outputStream.flush();
            BufferedReader reader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
            String outputstr;
            while ((outputstr = reader.readLine()) != null) {
                sb.append(outputstr);
            }
        } catch (IOException e) {
            LOG.error("外部接口调用失败，url: {}; param: {}", path, param, e);
        }
        return sb.toString();
    }

    @Override
    public RefundApplyDO getRefundApply(Long applyId) {
        LOG.info("query refundApply,applyId: {}", applyId);
        try {
            return refundApplyDao.getRefundApplyById(applyId);
        } catch (Exception e) {
            LOG.error("query refundApply error,applyId: {}", applyId, e);
        }
        return null;
    }

    @Override
    public boolean reApply(RefundApplyDO refundApplyDO) {
        LOG.info("update refundApply status,applyId: {},status: {}", refundApplyDO.getId(), refundApplyDO.getStatus());
        try {
            int result = refundApplyDao.updateRefundStatus(refundApplyDO);
            if (result > 0) {
                return true;
            }
        } catch (Exception e) {
            LOG.error("update refundApply status error,applyId: {}", refundApplyDO.getId(), e);
        }
        return false;
    }

}
